/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2).
 * This file is part of JBeat.
 * 
 *     JBeat is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     JBeat is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with JBeat.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Irineu Martins de Lima Moura (imlm2) - initial API and implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.util.options;

import java.util.*;

import br.ufpe.cin.imlm2.util.ParsingException;

public class OptionsParser extends Options {

	public static final String RUNTIME_EXCEPTIONS = "runtimeExceptions";
	public static final String CLASS_PATH = "classPath";
	public static final String CP = "cp";
	public static final String HAND_INSP = "handInsp";
	public static final String HANDLER_INSPECTION = "handlerInspection";

	public static final String VERBOSE = "verbose";
	public static final String TIME = "time";
	public static final String CLEAN_SOOT_OUTPUT = "cleanSootOutput";
	public static final String ALLOW_MISSING_DEPENDENCIES = "allowMissingDependencies";

	private OptionsParser() {
		super();
		this.options.add(new Option(HANDLER_INSPECTION, HAND_INSP,
				"Tells if a handler inspection analysis should be executed.",
				Boolean.TRUE, Boolean.class, null));
		this.options.add(new Option(RUNTIME_EXCEPTIONS, "rtExcep",
				"Tells if an analysis to determine if exceptions thrown by"
						+ " the runtime instructions should be execute.",
				Boolean.TRUE, Boolean.class, null));
		this.options.add(new Option(CLASS_PATH, CP,
				"Classpath for the application being analyzed.",
				new ArrayList<String>(), List.class, String.class));
		this.options.add(new Option(TIME, null,
				"If set to true shows soot debug output for running times.",
				Boolean.FALSE, Boolean.class, null));
		this.options.add(new Option(CLEAN_SOOT_OUTPUT, "clean",
				"Cleans soot output on exit.", Boolean.TRUE, Boolean.class,
				null));
		this.options
				.add(new Option(
						ALLOW_MISSING_DEPENDENCIES,
						"allowPhantoms",
						"Allows the analysis to go on even when there are missing dependencies",
						Boolean.FALSE, Boolean.class, null));
	}

	public static final String PARAM_PREFIX = "--";
	public static final String SOOT_ARGUMENT_PREFIX = "-";
	public static final String ARGUMENT_SEPARATOR = ":";

	/*
	 * Parameters representations
	 */

	public static final String SHOW_USAGE_PARAM = "help";

	/*
	 * Boolean Parameters
	 */
	private boolean showUsage = false;

	private String mainClass = "";

	private static OptionsParser INSTANCE;

	public static OptionsParser getInstance() {
		if (INSTANCE == null)
			INSTANCE = new OptionsParser();
		return INSTANCE;
	}

	public boolean isHandlerInspection() {
		return (Boolean) this.getOption(HAND_INSP).getValue();
	}

	public boolean isShowUsage() {
		return showUsage;
	}

	public void setShowUsage(boolean showUsage) {
		this.showUsage = showUsage;
	}

	public boolean isAnalyzeRuntime() {
		return (Boolean) this.getOption(RUNTIME_EXCEPTIONS).getValue();
	}

	@SuppressWarnings("unchecked")
	public List<String> getClasspath() {
		return (List<String>) this.getOption(CLASS_PATH).getValue();
	}

	public String getMainClass() {
		return mainClass;
	}

	public void setMainClass(String mainClass) {
		this.mainClass = mainClass;
	}

	public boolean isVerbose() {
		return (Boolean) this.getOption(VERBOSE).getValue();
	}

	public boolean isTime() {
		return (Boolean) this.getOption(TIME).getValue();
	}

	public boolean isCleanSootOutput() {
		return (Boolean) this.getOption(CLEAN_SOOT_OUTPUT).getValue();
	}

	public boolean isAllowMissingDependencies() {
		return (Boolean) this.getOption(ALLOW_MISSING_DEPENDENCIES).getValue();
	}

	public void parse(String[] args) throws ParsingException {
		List<String> argsList = new ArrayList<String>(Arrays.asList(args));
		String firstArg = argsList.get(0);
		if (firstArg.equals((OptionsParser.PARAM_PREFIX + SHOW_USAGE_PARAM))) {
			setShowUsage(true);
			return;
		}
		setMainClass(firstArg);
		argsList.remove(0);
		Map<String, String> options = new HashMap<String, String>();
		for (String str : argsList) {

			if (str.startsWith(PARAM_PREFIX)) {
				str = str.replaceFirst(PARAM_PREFIX, "");
				String[] parts = str.split(ARGUMENT_SEPARATOR, 2);
				if (parts.length != 2)
					throw new ParsingException("Unable to parse token: " + str);
				options.put(parts[0], parts[1]);
			} else
				throw new ParsingException("Unable to parse token: " + str);

		}

		try {
			this.parse(options);
		} catch (NoSuchOptionException e) {
			throw new ParsingException("Unkown argument " + e.getInvalidOtion());
		}

	}

}
